/*
 * Copyright 2023-2085 xujin <ceo@itgrail.com>
 *
 * Licensed under the Grail License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://itgrail.com/docs?id=3&catalogId=87
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.itgrail.pilot.app.command.query;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.itgrail.grail.cqrs.annotation.CmdHandler;
import com.itgrail.grail.cqrs.command.CommandExecutorI;
import com.itgrail.grail.dto.ResultData;
import com.itgrail.pilot.app.command.cmo.query.DetailOkrSettingCmd;
import com.itgrail.pilot.app.command.co.ClassifyDetailCO;
import com.itgrail.pilot.domain.user.contant.OkrConstants;
import com.itgrail.pilot.infrastructure.tunnel.db.dao.*;
import com.itgrail.pilot.infrastructure.tunnel.db.dataobject.*;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @Desc: 获取okr设定
 */
@CmdHandler
public class DetailOkrSettingCmdExe implements CommandExecutorI<ResultData<ClassifyDetailCO>, DetailOkrSettingCmd> {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ObjectiveMapper objectiveMapper;

    @Autowired
    private UserOkrAlignMapper userOkrAlignMapper;

    @Autowired
    private KeyResultMapper keyResultMapper;

    @Autowired
    private DepartmentMapper departmentMapper;

    @Autowired
    private DepartmentUserMapper departmentUserMapper;

    @Override
    public ResultData execute(DetailOkrSettingCmd cmd) {
        ResultData resultData = ResultData.success(null);
        check(cmd);
        resultData.setData(getFillRate(cmd.getPeriodId(), cmd.getDeptId()));
        return resultData;
    }

    protected boolean check(DetailOkrSettingCmd cmd) {
        Preconditions.checkNotNull(cmd, "cmd不能为null");
        Preconditions.checkNotNull(cmd.getPeriodId(), "周期id不能为null");
        return true;
    }

    /**
     * 获取okr填写率
     *
     * @return
     */
    protected Map<String, Object> getFillRate(Integer periodId, Integer deptId) {
        Map<String, Object> result = new HashMap<>();
        List<Map<String, Object>> deptRates = new ArrayList<>();
        //OKR填写率
        int fillRate = 0;
        //okr对齐率
        int alignRate = 0;
        BigDecimal bigDecimal = new BigDecimal(100);
        Map<String, Object> chargeObjective = new HashMap<>();
        //noObjective           未设置OKR 人员数量
        chargeObjective.put("noObjective", 0);
        //oneAndFiveObjective   1-5个OKR 人员数量
        chargeObjective.put("oneAndFiveObjective", 0);
        //mostFiveObjective     5个以上OKR 人员数量
        chargeObjective.put("mostFiveObjective", 0);
        chargeObjective.put("userCount", 0);

        //目标 O 下的 KR 数量分布
        Map<String, Object> objectiveKeyResultMap = new HashMap<>();
        Integer noObjectiveNum = 0;
        Integer oneAndTwoObjectiveNum = 0;
        Integer threeAndFiveObjectiveNum = 0;
        Integer mostFiveObjectiveNum = 0;
        Integer objectiveNum = 0;

        //OKR对齐量
        Map<String, Object> objectiveAlignMap = new HashMap<>();
        objectiveAlignMap.put("align", 0);
        objectiveAlignMap.put("noAlign", 0);
        List<Integer> deptIdList = Lists.newArrayList();
        if (deptId != null) {
            deptIdList.add(deptId);
        }
        //查询部门下的用户数量
        Long userCount = departmentUserMapper.selectUserCount(deptIdList);
        if (userCount > 0 || null != deptId) {
            Map<Integer, List<Integer>> deptUserNum = countNewDeptUserNum(deptId);
            List<Integer> deptUserIds = new ArrayList<>();
            for (Map.Entry<Integer, List<Integer>> entry : deptUserNum.entrySet()) {
                deptUserIds.addAll(entry.getValue());
            }
            List<Integer> userIds = deptUserIds.stream().distinct().collect(Collectors.toList());
            //查询当前周期下  该部门人员填写的OKR数量
            chargeObjective.put("userCount", deptUserIds.size());
            QueryWrapper<ObjectiveDO> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("okr_period_id", periodId);
            queryWrapper.eq("deleted", OkrConstants.NOT_DELETED);
            List<ObjectiveDO> objectiveDOS = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(deptUserIds)) {
                queryWrapper.in("created", deptUserIds);
                objectiveDOS = objectiveMapper.selectList(queryWrapper);
            }
            //查询当前部门用户填写的o的数量
            if (CollectionUtils.isNotEmpty(objectiveDOS)) {
                objectiveNum = objectiveDOS.size();
                //根据userId对o进行分组
                Map<Integer, List<ObjectiveDO>> userObjectives = objectiveDOS.stream().collect(Collectors.groupingBy(ObjectiveDO::getCreated));
                BigDecimal objectiveNums = new BigDecimal(userObjectives.size());
                BigDecimal userNums = new BigDecimal(userIds.size());
                BigDecimal fillRateDivide = objectiveNums.divide(userNums, 2, BigDecimal.ROUND_UP);
                fillRate = fillRateDivide.multiply(bigDecimal).intValue();
                chargeObjective.put("noObjective", userIds.size() - userObjectives.size());
                List<Integer> mostFiveList = new ArrayList<>();
                for (Map.Entry<Integer, List<ObjectiveDO>> entry : userObjectives.entrySet()) {
                    Integer key = entry.getKey();
                    List<ObjectiveDO> value = entry.getValue();
                    if (CollectionUtils.isNotEmpty(value) && value.size() > 5) {
                        mostFiveList.add(key);
                    }
                }
                chargeObjective.put("oneAndFiveObjective", userObjectives.size() - mostFiveList.size());
                chargeObjective.put("mostFiveObjective", mostFiveList.size());
                //根据o 查询对应的keyResult的数量   统计keyResult的报表
                QueryWrapper<KeyResultDO> keyResultDOQueryWrapper = new QueryWrapper<>();
                keyResultDOQueryWrapper.eq("deleted", OkrConstants.NOT_DELETED);
                keyResultDOQueryWrapper.in("objective_id", objectiveDOS.stream().map(ObjectiveDO::getId).collect(Collectors.toList()));
                List<KeyResultDO> keyResultDOS = keyResultMapper.selectList(keyResultDOQueryWrapper);
                if (CollectionUtils.isNotEmpty(keyResultDOS)) {
                    Map<Integer, List<KeyResultDO>> collect = keyResultDOS.stream().collect(Collectors.groupingBy(KeyResultDO::getObjectiveId));

                    for (int i = 0; i < objectiveDOS.size(); i++) {
                        List<KeyResultDO> resultDOS = collect.get(objectiveDOS.get(i).getId());
                        if (CollectionUtils.isNotEmpty(resultDOS)) {
                            if (resultDOS.size() >= 1 && resultDOS.size() <= 2) {
                                oneAndTwoObjectiveNum += 1;
                            }
                            if (resultDOS.size() >= 3 && resultDOS.size() <= 5) {
                                threeAndFiveObjectiveNum += 1;
                            }
                            if (resultDOS.size() > 5) {
                                mostFiveObjectiveNum += 1;
                            }
                        } else {
                            noObjectiveNum += 1;
                        }
                    }
                }
                QueryWrapper<UserOkrAlignDO> userOkrAlignDOQueryWrapper = new QueryWrapper<>();
                userOkrAlignDOQueryWrapper.eq("okr_period", periodId);
                userOkrAlignDOQueryWrapper.eq("deleted", OkrConstants.NOT_DELETED);
                List<UserOkrAlignDO> userOkrAlignDOS = userOkrAlignMapper.selectList(userOkrAlignDOQueryWrapper);
                if (CollectionUtils.isNotEmpty(userOkrAlignDOS)) {
                    List<Integer> allAlignOkr = new ArrayList<>();
                    for (UserOkrAlignDO userOkrAlignDO : userOkrAlignDOS) {
                        if (null != userOkrAlignDO && userIds.contains(userOkrAlignDO.getStartUserId())) {
                            allAlignOkr.add(userOkrAlignDO.getStartObjectiveId());
                        }
                        if (null != userOkrAlignDO && userIds.contains(userOkrAlignDO.getAcceptUserId())) {
                            allAlignOkr.add(userOkrAlignDO.getAcceptObjectiveId());
                        }
                    }
                    allAlignOkr = allAlignOkr.stream().distinct().collect(Collectors.toList());
                    BigDecimal allAlignOkrNums = new BigDecimal(allAlignOkr.size());
                    BigDecimal objectiveDONums = new BigDecimal(objectiveDOS.size());
                    BigDecimal alignRateDivide = allAlignOkrNums.divide(objectiveDONums, 2, BigDecimal.ROUND_UP);
                    alignRate = alignRateDivide.multiply(new BigDecimal(100)).intValue();
                    objectiveAlignMap.put("align", allAlignOkr.size());
                    objectiveAlignMap.put("noAlign", objectiveDOS.size() - allAlignOkr.size());
                } else {
                    objectiveAlignMap.put("align", 0);
                    objectiveAlignMap.put("noAlign", objectiveDOS.size());
                }
            }
        }
        objectiveKeyResultMap.put("noObjectiveNum", noObjectiveNum);
        objectiveKeyResultMap.put("oneAndTwoObjectiveNum", oneAndTwoObjectiveNum);
        objectiveKeyResultMap.put("threeAndFiveObjectiveNum", threeAndFiveObjectiveNum);
        objectiveKeyResultMap.put("mostFiveObjectiveNum", mostFiveObjectiveNum);
        objectiveKeyResultMap.put("objectiveNum", objectiveNum);
        //查询部门下的用户
        Map<Integer, List<Integer>> deptUserCount = countNewDeptUserNum(deptId);
        for (Map.Entry<Integer, List<Integer>> entry : deptUserCount.entrySet()) {
            Integer deptIdInfo = entry.getKey();
            List<Integer> userIds = entry.getValue();
            Map<String, Object> map = new HashMap<>();
            int rate = 0;
            if (CollectionUtils.isNotEmpty(userIds)) {
                QueryWrapper<ObjectiveDO> queryWrapper = new QueryWrapper<>();
                queryWrapper.in("created", userIds);
                List<ObjectiveDO> objectiveDOS = objectiveMapper.selectList(queryWrapper);
                List<Integer> userIdInfos = objectiveDOS.stream().map(ObjectiveDO::getCreated).distinct().collect(Collectors.toList());
                BigDecimal userNums = new BigDecimal(userIds.size());
                BigDecimal objectiveDONums = new BigDecimal(userIdInfos.size());
                BigDecimal alignRateDivide = objectiveDONums.divide(userNums, 2, BigDecimal.ROUND_UP);
                rate = alignRateDivide.multiply(new BigDecimal(100)).intValue();
            }
            DepartmentDO departmentDO = departmentMapper.selectById(deptIdInfo);
            map.put("name", departmentDO != null ? departmentDO.getName() : "");
            map.put("rate", rate);
            deptRates.add(map);
        }
        //OKR 部门填写率
        result.put("deptRate", deptRates);
        //OKR 填写率
        result.put("okrFillRate", fillRate);
        //OKR 对齐率
        result.put("okrAlignRate", alignRate);
        //每个人负责的目标 O 数量分布
        result.put("chargeObjective", chargeObjective);
        //目标 O 下的 KR 数量分布
        result.put("objectiveKeyResultMap", objectiveKeyResultMap);
        //OKR对齐量
        result.put("objectiveAlignMap", objectiveAlignMap);
        return result;
    }

    private Map<Integer, List<Integer>> countNewDeptUserNum(Integer deptId) {
        Map<Integer, List<Integer>> deptUserCount = new HashMap<>();
        if (deptId != null) {
            deptUserCount.put(deptId, departmentUserMapper.selectUserIdList(Lists.newArrayList(deptId)));
        }
        //部门信息
        List<DepartmentDO> departmentList = departmentMapper.selectListByParam(deptId);
        List<Integer> deptIdList = departmentList.stream().map(DepartmentDO::getId).collect(Collectors.toList());
        List<DepartmentUserDO> deptUserList = departmentUserMapper.selectUserList(deptIdList);
        Map<Integer, List<DepartmentUserDO>> deptUserMap = deptUserList.stream().collect(Collectors.groupingBy(DepartmentUserDO::getDepartmentId));
        //递归查询子部门的所有部门
        for (DepartmentDO departmentDO : departmentList) {
            Integer id = departmentDO.getId();
            if (deptUserMap.containsKey(id)) {
                List<Integer> userIdList = deptUserMap.get(id).stream().map(DepartmentUserDO::getUser)
                        .filter(data -> data.getUserId() != null)
                        .map(UserDO::getUserId).distinct().collect(Collectors.toList());
                userIdList.addAll(getTotalUserId(id, departmentList, deptUserMap));
                deptUserCount.put(id, userIdList.stream().distinct().collect(Collectors.toList()));
            } else {
                deptUserCount.put(id, new ArrayList<>());
            }
        }
        return deptUserCount;
    }

    private List<Integer> getTotalUserId(Integer parentId, List<DepartmentDO> departmentList, Map<Integer, List<DepartmentUserDO>> deptUserMap) {
        List<Integer> dataList = new ArrayList<>();
        for (DepartmentDO departmentDO : departmentList) {
            if (parentId.equals(departmentDO.getParentId())) {
                Integer id = departmentDO.getId();
                if (deptUserMap.containsKey(id)) {
                    List<Integer> userIdList = deptUserMap.get(id).stream().map(DepartmentUserDO::getUser)
                            .filter(data -> data.getUserId() != null)
                            .map(UserDO::getUserId).distinct().collect(Collectors.toList());
                    dataList.addAll(userIdList);
                    dataList.addAll(getTotalUserId(id, departmentList, deptUserMap));
                }
            }
        }
        return dataList;
    }
}
